package gu.simplemq.jms;

import java.util.Timer;
import java.util.TimerTask;

import javax.jms.ExceptionListener;
import javax.jms.JMSException;

/**
 * JMS 重连接实现<br>
 * 通过实现{@link ExceptionListener}接口侦听连接异常，
 * 使用定时任务迟延执行重连接尝试直至连接成功
 * @author guyadong
 * @since 2.3.8
 */
class AutoReconnectAdapter implements ExceptionListener,JmsConstants{
	/**
	 * 重连接初始延迟时间(秒)
	 */
	private static final long START_RECONNECTDELAY = 1;
	/**
	 * 重连接最大延迟时间(秒)
	 */
	private static final long MAX_RECONNECT_DELAY = 128;
	/**
	 * Automatic reconnect timer
	 */
	private static final Timer reconnectTimer = new Timer("AMQP Reconnect"); 
	/**
	 * Reconnect delay, starts at 1 second
	 */
	private long reconnectDelay = START_RECONNECTDELAY; 
	private final JMSReconnectCallback jmsReconnectCallback;
	AutoReconnectAdapter(JMSReconnectCallback jmsReconnectCallback) {
		this.jmsReconnectCallback = jmsReconnectCallback;
	}

	@Override
	public void onException(JMSException exception) {
		if(null != jmsReconnectCallback) {
			try {
				jmsReconnectCallback.onConnectionLost();
				scheduleReconnectCycle();
			} catch (Exception e) {
				logger.error(e.getMessage(),e);
			}
		}
	}
	/**
	 * 尝试将客户端重新连接到服务器。如果成功，它将确保不再计划重新连接。
	 * 但是，如果连接失败，延迟将增加一倍(最大128秒)，并将在延迟后重新安排重新连接。
	 */
	private void attemptReconnect() {
		if(null != jmsReconnectCallback) {
			try {
				jmsReconnectCallback.tryReconnecting();
				// restore to default value
				reconnectDelay = START_RECONNECTDELAY;
			}	catch (Exception e) {
				if(e instanceof JMSException || e.getCause() instanceof JMSException ) {
					reconnectDelay = Math.min(reconnectDelay*2, MAX_RECONNECT_DELAY);
					scheduleReconnectCycle();
				}else {
					logger.error(e.getMessage(),e);
				}
			}
		}
	}
	/**
	 * 安排在{@link #reconnectDelay}指定的延迟时间后执行重连接尝试
	 */
	private void scheduleReconnectCycle() {
		logger.info("{} Scheduling reconnect timer, delay {} seconds",jmsReconnectCallback.ownerName(), reconnectDelay);
		reconnectTimer.schedule(new TimerTask() {
			@Override
			public void run() {
				attemptReconnect();
			}}, reconnectDelay*1000);
	}
	
}
